﻿namespace MinesweeperGame
{
    using System;

    public class GameEngine
    {
        private readonly int mineFieldRows;
        private readonly int mineFieldCols;
        private readonly int minesCount;

        private bool[,] displayCell;
        private bool[,] isOpenedCell;
        private bool[,] isMine;

        public GameEngine(int mineFieldRows, int mineFieldCols, int minesCount)
        {
            this.mineFieldRows = mineFieldRows;
            this.mineFieldCols = mineFieldCols;
            this.minesCount = minesCount;

            int rows = this.mineFieldRows;
            int cols = this.mineFieldCols;
            this.displayCell = new bool[rows, cols];
            this.isOpenedCell = new bool[rows, cols];
            this.isMine = new bool[rows, cols];
        }

        public int CountOpenCells()
        {
            int openFieldsCount = 0;
            for (int row = 0; row < this.mineFieldRows; row++)
            {
                for (int col = 0; col < this.mineFieldCols; col++)
                {
                    if (this.isOpenedCell[row, col])
                    {
                        openFieldsCount++;
                    }
                }
            }

            return openFieldsCount;
        }

        public void OpenCell(int row, int col)
        {
            this.isOpenedCell[row, col] = true;
            this.displayCell[row, col] = true;
        }

        public bool CheckCellForMine(int row, int col)
        {
            if (this.isMine[row, col])
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void OpenAllCells()
        {
            for (int i = 0; i < this.mineFieldRows; i++)
            {
                for (int j = 0; j < this.mineFieldCols; j++)
                {
                    this.displayCell[i, j] = true;
                }
            }
        }

        public bool ValidateCell(int row, int col)
        {
            if (this.isOpenedCell[row, col])
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        public void RenderGame()
        {
            this.DisplayColumnNumbers();
            this.DisplayLine();
            this.DisplayGameField();
            this.DisplayLine();
        }

        public void StartNewGame()
        {
            this.CreateGameField();
            this.GenerateMines();
            this.RenderGame();
        }

        private void CreateGameField()
        {
            for (int row = 0; row < this.mineFieldRows; row++)
            {
                for (int col = 0; col < this.mineFieldCols; col++)
                {
                    this.displayCell[row, col] = false;
                    this.isOpenedCell[row, col] = false;
                    this.isMine[row, col] = false;
                }
            }
        }

        private void GenerateMines()
        {
            Random random = new Random();
            for (int i = 0; i < this.minesCount; i++)
            {
                int row;
                int col;
                do
                {
                    row = random.Next(this.mineFieldRows);
                    col = random.Next(this.mineFieldCols);
                }
                while (this.isMine[row, col]);

                this.isMine[row, col] = true;
            }
        }
   
        private void DisplayGameField()
        {
            string decoration = "| ";

            for (int row = 0; row < this.mineFieldRows; row++)
            {
                // Display row number
                Console.Write("{0} ", row);
                Console.Write(decoration);
                for (int col = 0; col < this.mineFieldCols; col++)
                {
                    this.DisplayCell(row, col);
                }

                Console.WriteLine(decoration);
            }
        }

        private void DisplayLine()
        {
            Console.Write("    ");
            for (int i = 0, len = this.mineFieldCols * 2; i < len; i++)
            {
                Console.Write("-");
            }

            Console.WriteLine();
        }

        private void DisplayColumnNumbers()
        {
            // Display column numbers
            Console.Write("    ");
            for (int i = 0; i < this.mineFieldCols; i++)
            {
                Console.Write("{0} ", i);
            }

            Console.WriteLine();
        }

        private void DisplayCell(int row, int col)
        {
            if (this.displayCell[row, col])
            {
                if (this.isMine[row, col])
                {
                    Console.Write("* ");
                }
                else if (this.isOpenedCell[row, col])
                {
                    Console.Write("{0} ", this.CountNeighborMines(row, col));
                }
                else
                {
                    Console.Write("- ");
                }
            }
            else
            {
                Console.Write("? ");
            }
        }

        private int CountNeighborMines(int row, int col)
        {
            int minesCount = 0;
            for (int i = -1; i < 2; i++)
            {
                for (int j = -1; j < 2; j++)
                {
                    if (j == 0 && i == 0)
                    {
                        continue;
                    }
                    else if (this.CheckMineFieldBounds(row + i, col + j) && this.isMine[row + i, col + j])
                    {
                        minesCount++;
                    }
                }
            }

            return minesCount;
        }

        private bool CheckMineFieldBounds(int row, int col)
        {
            return (0 <= row && row < this.mineFieldRows) && (0 <= col && col < this.mineFieldCols);
        }
    }
}
